﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Imagine
{
    public enum eAnimationType
    {
        NONE,
        ONCE,
        LOOP
    }

    public struct AnimInfo
    {
        public AnimInfo( int index_, int frameCount_, eAnimationType type )  
        {            
            SpriteSheetIndex = index_;
            FrameCount = frameCount_;
            eAnimationType = type;
        }        
        public int              SpriteSheetIndex;
        public int              FrameCount;
        public eAnimationType    eAnimationType;
    }

    public class AnimatedSprite : Sprite
    {
        //Use this constructor in case you want to give it simple Sprite like functionality
        public AnimatedSprite( string texture ) : base(texture)
        {
            m_animations = new List<AnimInfo>();
            m_frameIndex = 0;           
            m_frameCount = 1;            
            m_sourceRectangle.Width = m_texture.Width;
            m_sourceRectangle.Height = m_texture.Height;
            m_frameTime = 100;
            m_animType = eAnimationType.NONE;
            m_playing = false; 
            m_animIndex = 0;
            m_spriteSheetIndex = 0;
        }
        //Use this if you want a normal Animation
        public AnimatedSprite(string texture, int frameCount, int rows, int columns) : base(texture)
        {
            m_animations = new List<AnimInfo>();
            m_frameIndex = 0;           
            m_frameCount = frameCount;            
            FrameSize = new Vector2(m_texture.Width/columns,m_texture.Height/rows);
            m_frameTime = 100;
            m_animType = eAnimationType.ONCE;
            m_playing = false; 
            m_animIndex = 0;
            m_spriteSheetIndex = 0;
        }

        #region Private_Parts        
        List<AnimInfo>            m_animations;
        protected int             m_spriteSheetIndex;
        protected int             m_frameCount;
        protected int             m_frameIndex;
        protected float           m_elapsedTime;
        protected int             m_frameTime;        //Delay time b/w each frame
        protected bool            m_playing;
        protected int             m_animIndex;        //Delay time b/w each frame
        protected eAnimationType   m_animType;

        //Uses Index to generate the Source (XY) into the SpriteSheet
        Point GenerateXY(int index)
        {
            #region Explanation
            //For Me, incase i forget how i did this
            /* Suppose there are 5 cols in each row and total 3 rows, so totalframes = 3*5 = 15
             * and our index is 13
             * so int rowNum = 13/5 = 2 which tells us we have to go 2 rows down
             * and int colNum = 13%5 = 3 which tells us the column num 
             * index = 3 - 1 = 2
             */ 
            #endregion
            
            int framesPerRow = Math.Max( m_texture.Width/m_sourceRectangle.Width, 1);
            int rowNum = index / framesPerRow;
            int colNum = index % framesPerRow;           

            return (new Point( colNum * m_sourceRectangle.Width, rowNum * m_sourceRectangle.Height ));

        }           
        #endregion

        #region Properties
        public int FrameCount
        {   
            get { return m_frameCount;   }
            set { m_frameCount = value;  }
        }

        public int CurrentFrame
        {
            get {return m_frameIndex;   }
            set {m_frameIndex = value;  }
        }
        
        public int FrameTime
        {
            get { return m_frameTime;   }
            set { m_frameTime = value;  }
        }

        public eAnimationType eAnimationType
        {
            get { return m_animType;    }
            set { m_animType = value;   }
        }

        public bool Playing  
        {
            get { return m_playing;     }
        }

        public int SpriteSheetIndex
        {
            get { return m_spriteSheetIndex;    }
            set { m_spriteSheetIndex = value;   }
        }

        public bool Paused
        {
            get { return !m_playing;    }
        }
        #endregion

        public override bool Update(GameTime time)
        {
            if(m_animations.Count > 0)
            {
                AnimInfo info = m_animations[m_animIndex];
                m_frameCount = info.FrameCount;
                m_spriteSheetIndex = info.SpriteSheetIndex;
                m_animType = info.eAnimationType;
            }
            if( m_playing )
                m_elapsedTime += (float)time.ElapsedGameTime.TotalMilliseconds;  

            if(m_elapsedTime > m_frameTime)
            {
                m_elapsedTime = 0.0f;       //Reset the timer
                //now increment the frameIndex, depending on what kind of animation it is
                switch(m_animType)
                {
                    case eAnimationType.ONCE:
                        {
                            m_frameIndex = Math.Min( m_frameIndex + 1, m_frameCount - 1);
                            if(m_frameIndex == m_frameCount )
                            {
                                m_frameIndex = 0;
                                m_playing = false;
                            }
                            break;
                        }

                    case eAnimationType.LOOP:
                        {
                            m_frameIndex = ( m_frameIndex + 1 ) % m_frameCount;
                            break;
                        }

                }
            }
  
            return true;
        }

        public override bool Draw(GameTime time)
        {            
            //Calculate the new SourceRect and call the Sprite's(base class's) Draw()            
            m_sourceRectangle.Location = GenerateXY( m_spriteSheetIndex + m_frameIndex);
            base.Draw(time);
            return true;
        }

        public void AddAnimation(AnimInfo animatio_)     { m_animations.Add(animatio_);  }
        public void Play(int animIndex)  { m_playing = true; m_animIndex = animIndex;    }
        public void Pause() { m_playing = false;    }
        public void Stop()  
        { 
            m_playing = false; 
            m_frameIndex = 0;
        }       
    }
}

